/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_auth.h"
#include <stddef.h>
#include <string.h>
#include "securec.h"
#include "cJSON.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/md.h"
#include "mbedtls/pk.h"
#include "mbedtls/sha256.h"
#include "ola_cloud.h"
#include "ola_error.h"
#include "ola_log.h"
#include "ola_def.h"
#include "ola_base_utils.h"
#include "ola_link.h"
#include "ola_security.h"
#include "ola_random.h"
#include "ola_device_model.h"
#include "ola_data.h"
#include "ola_sdk.h"

typedef enum {
    DEVICE_AUTH_STATE_SEND_DEVICE_AUTH_REQUEST,
    DEVICE_AUTH_STATE_SEND_CLOUD_CERTIFY_REQUEST,
    DEVICE_AUTH_STATE_WAIT_CLOUD_RESPONSE,
    DEVICE_AUTH_STATE_FINISH,
    DEVICE_AUTH_STATE_FAILED,
} DeviceAuthState;

/* 证书链在打桩时候已经做过Base64 编码处理，这里不需要再做一次，联调完需要恢复 */
#define AUTH_CERT_CHAIN_NEED_BASE64 0

/* 共享秘钥认证全局变量长度 */
#define AUTH_LICENSE_LEN 32
#define DEV_AUTH_BUFF_MAX_LEN 512
#define OLA_DEV_AUTH_HS_LEN 64
#define OLA_DEV_AUTH_RC_LEN 8
#define OLA_DEV_AUTH_PSK_LEN 64
/* 共享秘钥认证结构体 */
typedef struct {
    unsigned char license[AUTH_LICENSE_LEN + 1];           /* 应用终端的license */
    unsigned char devAuthRc[OLA_DEV_AUTH_RC_LEN + 1];      /* 应用终端随机值Rc */
    unsigned char localAuthHs[OLA_DEV_AUTH_HS_LEN + 1];    /* 应用终端生成校验凭证Hc’ */
    unsigned char devAuthNewPsk[OLA_DEV_AUTH_PSK_LEN + 1]; /* 应用终端计算的NewPSK */
} OlaAuthLicenseParam;

/* 证书相关全局变量长度 */
#define DEV_CERTCHAIN_LEN 4096
#define DEV_SIGNATURE_LEN 512
#define DEV_PRIVATE_KEY_LEN 512
/* 证书认证结构体 */
typedef struct {
    unsigned char certChain[DEV_CERTCHAIN_LEN + 1];    /* 配置器的证书链做Base64处理后的文本 */
    unsigned char privateKey[DEV_PRIVATE_KEY_LEN + 1]; /* 证书私钥 */
    unsigned char signature[DEV_SIGNATURE_LEN + 1];    /* 应用终端对challengeS用私钥进行的签名做Base64处理后的文本 */
} OlaAuthCertParam;

static OlaDevAuthType g_devAuthType = OLA_DEV_AUTH_TYPE_CERT;
static DeviceAuthState g_authState = DEVICE_AUTH_STATE_SEND_DEVICE_AUTH_REQUEST;
static OlaAuthLicenseParam g_licenseData = { 0 };
static OlaAuthCertParam g_certData = { 0 };

/* 定时器开始启动时间 */
static unsigned long long g_startWaitTime = 0;

static OlaDevAuthType OlaGetDevAuthType(void)
{
    return g_devAuthType;
}

/* 暂只支持证书认证方式 */
static int OlaSetDevAuthType(OlaDevAuthType type)
{
    if (type != OLA_DEV_AUTH_TYPE_CERT && type != OLA_DEV_AUTH_TYPE_LICENSE) {
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    g_devAuthType = type;
    return OLA_OK;
}

static DeviceAuthState OlaGetDeviceAuthState(void)
{
    return g_authState;
}

static void OlaSetDeviceAuthState(DeviceAuthState state)
{
    g_authState = state;
    OlaLogInfo("set dev auth [%d]", state);
}

/* mbedtls认证签名 */
static int OlaGetSignature(unsigned char *signature, size_t *slen, const unsigned char *challenge,
    unsigned int challengeLen)
{
    if (signature == NULL || slen == NULL) {
        OlaLogError("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    mbedtls_pk_context ctx_pk;
    mbedtls_pk_init(&ctx_pk);
    int ret = mbedtls_pk_parse_key(&ctx_pk, g_certData.privateKey, strlen((char *)g_certData.privateKey) + 1, NULL, 0);
    if (ret != 0) {
        OlaLogError("mbedtls fail returned [%d]", ret);
        goto EXIT;
    }

    unsigned char output[OLA_SHA256_HEX_LEN] = {0};
    ret = OlaSha256(challenge, challengeLen, output);
    if (ret != OLA_OK) {
        OlaLogError("Sha256 fail");
        goto EXIT;
    }

    ret = mbedtls_pk_sign(&ctx_pk, MBEDTLS_MD_SHA256, output, 0, signature, slen, NULL, NULL);
    if (ret != 0) {
        OlaLogError("mbedtls fail returned [%d]", ret);
        goto EXIT;
    }
EXIT:
    mbedtls_pk_free(&ctx_pk);
    return ret;
}

static int OlaProcCertDeviceAuthRsp(cJSON *json)
{
    char *challengeS = OlaGetJsonString(json, OLA_STRING_CHALLENGE);
    if ((challengeS == NULL) || (strlen(challengeS) == 0) || (strlen(challengeS) > OLA_DEV_AUTH_CHALLENGE_LEN)) {
        OlaLogError("challengeS err");
        return OLA_CJSON_ERR_GET_STRING;
    }

    /* T-OLA-000X.2:202X 7.1 应用终端将challengeS用私钥进行签名 */
    unsigned char signOut[DEV_SIGNATURE_LEN] = {0};
    size_t signOutLen = DEV_SIGNATURE_LEN;
    if (OlaGetSignature(signOut, &signOutLen, (unsigned char *)challengeS, strlen(challengeS)) != OLA_OK) {
        OlaLogError("signature data fail");
        return OLA_MBEDTLS_ERR_SIGNATURE;
    }

    OlaData signatureBase64In = { (unsigned char *)signOut, (unsigned int)signOutLen };
    OlaData signatureBase64out = { NULL, 0 };
    int ret = OlaEncodeBase64Data(&signatureBase64In, &signatureBase64out);
    if (ret != OLA_OK) {
        OlaLogError("encode error");
        return OLA_MBEDTLS_ERR_BASE64_ENCODE;
    }

    if (strcpy_s((char *)g_certData.signature, sizeof(g_certData.signature), (char *)signatureBase64out.data) != EOK) {
        OlaLogError("strcpy err");
        OlaDataFree(&signatureBase64out);
        return OLA_SECUREC_ERR_STRCPY;
    }
    OlaDataFree(&signatureBase64out);

    return OLA_OK;
}

static int OlaLicenseAuthCalcLocalHs(const unsigned char *cloudRs, unsigned int cloudRsLen)
{
    unsigned char localHsHex[OLA_SHA256_HEX_LEN] = {0};
    unsigned char keyHs[DEV_AUTH_BUFF_MAX_LEN + 1] = {0};
    unsigned char msgHs[DEV_AUTH_BUFF_MAX_LEN + 1] = {0};
    const OlaDeviceInfo *devinfo = OlaGetDevInfo();
    if (devinfo == NULL) {
        OlaLogWarning("get device info error");
        return OLA_DEVICE_ERR_GET_INFO;
    }

    int len = sprintf_s((char *)keyHs, sizeof(keyHs) / sizeof(keyHs[0]), "%s%s%s", g_licenseData.license, cloudRs,
        g_licenseData.devAuthRc);
    if (len <= 0) {
        OlaLogError("snprintf keyHs fail");
        return OLA_SECUREC_ERR_SNPRINTF;
    }

    len = sprintf_s((char *)msgHs, sizeof(msgHs) / sizeof(msgHs[0]), "%s%s%s", devinfo->prodId, devinfo->mac,
        OLA_STRING_SER_AUTH);
    if (len <= 0) {
        OlaLogError("snprintf msgHs fail");
        return OLA_SECUREC_ERR_SNPRINTF;
    }

    OlaData keyHsData = { (unsigned char *)keyHs, strlen((char *)keyHs) };
    OlaData msgHsData = { (unsigned char *)msgHs, strlen((char *)msgHs) };
    int ret = OlaComputeMdHmac(&keyHsData, &msgHsData, (unsigned char *)localHsHex);
    if (ret != OLA_OK) {
        OlaLogError("compute md hmac fail");
        return ret;
    }

    ret = OlaHexify(g_licenseData.localAuthHs, sizeof(g_licenseData.localAuthHs),
        localHsHex, OLA_SHA256_HEX_LEN, false);
    if (ret != OLA_OK) {
        OlaLogError("convert fail");
        return ret;
    }

    return OLA_OK;
}

static int OlaLicenseAuthCheckHcTruste(const unsigned char *cloudHc, unsigned int cloudHcLen)
{
    unsigned char localHcHex[OLA_SHA256_HEX_LEN] = {0};
    unsigned char localHc[OLA_SHA256_STRING_LEN + 1] = {0};
    unsigned char keyHc[DEV_AUTH_BUFF_MAX_LEN + 1] = {0};
    unsigned char msgHc[DEV_AUTH_BUFF_MAX_LEN + 1] = {0};
    const OlaDeviceInfo *devinfo = OlaGetDevInfo();
    if (devinfo == NULL) {
        OlaLogWarning("get device info error");
        return OLA_DEVICE_ERR_GET_INFO;
    }

    int len = sprintf_s((char *)keyHc, sizeof(keyHc) / sizeof(keyHc[0]), "%s%s", (unsigned char *)g_licenseData.license,
        (unsigned char *)g_licenseData.devAuthRc);
    if (len <= 0) {
        OlaLogError("snprintf keyHc fail");
        return OLA_SECUREC_ERR_SNPRINTF;
    }

    len = sprintf_s((char *)msgHc, sizeof(msgHc) / sizeof(msgHc[0]), "%s%s%s", devinfo->prodId, devinfo->mac,
        OLA_STRING_DEV_AUTH);
    if (len <= 0) {
        OlaLogError("snprintf msgHc fail");
        return OLA_SECUREC_ERR_SNPRINTF;
    }

    OlaData keyHsData = { keyHc, strlen((char *)keyHc) };
    OlaData msgHsData = { msgHc, strlen((char *)msgHc) };
    int ret = OlaComputeMdHmac(&keyHsData, &msgHsData, (unsigned char *)localHcHex);
    if (ret != OLA_OK) {
        OlaLogError("compute md hmac fail");
        return ret;
    }

    ret = OlaHexify(localHc, sizeof(localHc), localHcHex, OLA_SHA256_HEX_LEN, false);
    if (ret != OLA_OK) {
        OlaLogError("convert fail");
        return ret;
    }

    if (strncasecmp((char *)localHc, (char *)cloudHc, OLA_SHA256_STRING_LEN) != 0) {
        OlaLogError("hc not match, cloud Untrusted.");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }
    return OLA_OK;
}

static int OlaLicenseAuthParseCloudHcAndRs(cJSON *json, unsigned char *hc, unsigned int hcLen,
    unsigned char *rc, unsigned int rcLen)
{
    char *cloudHc = OlaGetJsonString(json, OLA_STRING_HC);
    if ((cloudHc == NULL) || (strlen(cloudHc) == 0) || (strlen(cloudHc) > OLA_DEV_AUTH_HC_LEN)) {
        OlaLogError("json parse err");
        return OLA_CJSON_ERR_GET_STRING;
    }

    if (strcpy_s((char *)hc, hcLen, cloudHc) != EOK) {
        OlaLogError("strcpy err");
        return OLA_SECUREC_ERR_STRCPY;
    }

    char *cloudRs = OlaGetJsonString(json, OLA_STRING_RS);
    if ((cloudRs == NULL) || (strlen(cloudRs) == 0) || (strlen(cloudRs) > OLA_DEV_AUTH_RS_LEN)) {
        OlaLogError("json parse err");
        return OLA_CJSON_ERR_GET_STRING;
    }

    if (strcpy_s((char *)rc, rcLen, cloudRs) != EOK) {
        OlaLogError("strcpy err");
        return OLA_SECUREC_ERR_STRCPY;
    }

    return OLA_OK;
}

static int OlaProcLicenseDeviceAuthRsp(cJSON *json)
{
    unsigned char cloudAuthHc[OLA_DEV_AUTH_HC_LEN + 1]; /* 认证中心验证凭证Hc */
    unsigned char cloudAuthRs[OLA_DEV_AUTH_RS_LEN + 1]; /* 云端随机值Rs */

    /* T-OLA-000X.2:202X 8.2 解析接入管理中心传过来的Hc,Rs */
    int ret = OlaLicenseAuthParseCloudHcAndRs(json, cloudAuthHc, sizeof(cloudAuthHc) / sizeof(cloudAuthHc[0]),
        cloudAuthRs, sizeof(cloudAuthRs) / sizeof(cloudAuthRs[0]));
    if (ret != OLA_OK) {
        return ret;
    }

    /* T-OLA-000X.2:202X 8.2 应用终端根据自己的Rc生成校验凭证Hc’,比较与收到的Hc是否一致,如一致则认为云端可信 */
    ret = OlaLicenseAuthCheckHcTruste(cloudAuthHc, strlen((char *)cloudAuthHc));
    if (ret != OLA_OK) {
        return ret;
    }

    /* T-OLA-000X.2:202X 8.2 根据收到的Rs和Rc，计算生成云端校验凭证Hs’ */
    ret = OlaLicenseAuthCalcLocalHs(cloudAuthRs, strlen((char *)cloudAuthRs));
    if (ret != OLA_OK) {
        return ret;
    }
    return OLA_OK;
}

static int OlaAuthParseErrCode(cJSON *json)
{
    cJSON *errCode = cJSON_GetObjectItem(json, OLA_STRING_ERRCODE);
    if (errCode == NULL) {
        OlaLogError("no errCode");
        return OLA_CJSON_ERR_PARSE;
    }

    if (errCode->valueint == OLA_OK) {
        return OLA_OK;
    }

    OlaLogError("err code: %d", errCode->valueint);

    switch (errCode->valueint) {
        case OLA_AUTH_CERTIFICATION_ERR: /* 设备证书认证失败 */
            return OLA_AUTH_CERTIFICATION_ERR;
        default:
            return OLA_AUTH_ERR_OTHERS;
    }

    return OLA_OK;
}

static int OlaCloudCertifyParseTicket(cJSON *json)
{
    char *ticket = OlaGetJsonString(json, OLA_STRING_TICKET);
    if ((ticket == NULL) || (strlen(ticket) == 0) || (strlen(ticket) > OLA_DEV_TICKET_LEN)) {
        OlaLogError("json parse ticket err");
        return OLA_CJSON_ERR_GET_STRING;
    }

    OlaData ticketData = { NULL, strlen(ticket) };
    ticketData.data = (unsigned char *)OlaMallocAndCopyStr(ticket, strlen(ticket));
    if ((ticketData.data == NULL) || (OlaSetData(OLA_DATA_TYPE_TICKET, &ticketData) != OLA_OK)) {
        OlaLogError("set cloud url failed");
        OlaDataFree(&ticketData);
        return OLA_DATA_ERR_SET_DATA;
    }
    OlaDataFree(&ticketData);
    return OLA_OK;
}

static int OlaLicenseAuthCalcDevicePsk(cJSON *json)
{
    unsigned char newPskHex[OLA_SHA256_HEX_LEN] = {0};

    /* 8.2　基于设备预共享密钥的认证需要更新psk,协议上未说明key长度 */
    char *key = OlaGetJsonString(json, OLA_STRING_CERITIFY_KEY);
    if ((key == NULL) || (strlen(key) == 0)) {
        OlaLogError("json parse ticket err");
        return OLA_CJSON_ERR_GET_STRING;
    }

    const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
    int ret = mbedtls_hkdf(md, (unsigned char *)key, strlen((char *)key), g_licenseData.license,
        strlen((char *)g_licenseData.license), (unsigned char *)OLA_STRING_OPCODE, strlen(OLA_STRING_OPCODE),
        (unsigned char *)newPskHex, OLA_SHA256_HEX_LEN);
    if (ret != OLA_OK) {
        OlaLogError("hkdf err");
        return OLA_CJSON_ERR_PARSE;
    }

    /* 8.2　终端生成PSK */
    ret = OlaHexify((unsigned char *)g_licenseData.devAuthNewPsk, sizeof(g_licenseData.devAuthNewPsk),
        newPskHex, OLA_SHA256_HEX_LEN, false);
    if (ret != OLA_OK) {
        OlaLogError("convert fail");
        return ret;
    }

    return OLA_OK;
}

static int OlaProcCloudCertifyRsp(const OlaData *payload)
{
    cJSON *json = cJSON_Parse((const char *)payload->data);
    if (json == NULL) {
        OlaLogError("json parse err");
        return OLA_CJSON_ERR_PARSE;
    }

    int ret = OlaAuthParseErrCode(json);
    if (ret != OLA_OK) {
        OlaLogError("parse auth errCode err");
        cJSON_Delete(json);
        return ret;
    }

    ret = OlaCloudCertifyParseTicket(json);
    if (ret != OLA_OK) {
        OlaLogError("parse ticket err");
        cJSON_Delete(json);
        return ret;
    }

    if (OlaGetDevAuthType() == OLA_DEV_AUTH_TYPE_LICENSE) {
        ret = OlaLicenseAuthCalcDevicePsk(json);
        if (ret != OLA_OK) {
            OlaLogError("calc psk err");
            cJSON_Delete(json);
            return ret;
        }
    }
    cJSON_Delete(json);
    return OLA_OK;
}

static int OlaProcDeviceAuthRsp(const OlaData *payload)
{
    cJSON *json = cJSON_Parse((const char *)payload->data);
    if (json == NULL) {
        OlaLogError("json parse err");
        return OLA_CJSON_ERR_PARSE;
    }

    int ret = OlaAuthParseErrCode(json);
    if (ret != OLA_OK) {
        OlaLogError("parse auth errcode err");
        cJSON_Delete(json);
        return ret;
    }

    if (OlaGetDevAuthType() == OLA_DEV_AUTH_TYPE_LICENSE) {
        ret = OlaProcLicenseDeviceAuthRsp(json);
    } else {
        ret = OlaProcCertDeviceAuthRsp(json);
    }

    cJSON_Delete(json);
    return ret;
}

static void OlaDeviceAuthRspHandler(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    if ((session == NULL) || (pkt == NULL) || !OlaIsValidCloudSession(session)) {
        OlaLogWarning("invalid param");
        return;
    }

    if (OlaGetDeviceAuthState() != DEVICE_AUTH_STATE_WAIT_CLOUD_RESPONSE) {
        OlaLogWarning("response in invalid state");
        return;
    }

    if (pkt->header.code != OLA_COAP_RESPONSE_CODE_CONTENT) {
        OlaLogWarning("coap response error[%d]", pkt->header.code);
        goto FAILED;
    }

    if ((pkt->payload.data == NULL) || (pkt->payload.len == 0)) {
        OlaLogWarning("empty response");
        goto FAILED;
    }

    if (OlaProcDeviceAuthRsp(&pkt->payload) != OLA_OK) {
        OlaLogWarning("parse dev auth resp error");
        goto FAILED;
    }

    OlaSetDeviceAuthState(DEVICE_AUTH_STATE_SEND_CLOUD_CERTIFY_REQUEST);
    return;
FAILED:
    OlaSetDeviceAuthState(DEVICE_AUTH_STATE_FAILED);
}

static int OlaAuthAddUdidToJson(cJSON *payloadJson)
{
    /* 获取udid */
    const OlaDeviceInfo *devInfo = OlaGetDevInfo();
    if ((devInfo == NULL) || (devInfo->udid) == NULL || (strlen((char *)devInfo->udid) == 0) ||
        (strlen((char *)devInfo->udid) > OLA_DEV_MODEL_MAX_UDID_LEN)) {
        OlaLogWarning("get udid error");
        return OLA_DEVICE_ERR_GET_INFO;
    }
    /* 内部函数调用点保证payloadJson不为NULL */
    if (cJSON_AddStringToObject(payloadJson, OLA_STRING_UDID, devInfo->udid) == NULL) {
        OlaLogError("add string error");
        return OLA_CJSON_ERR_ADD_STRING;
    }
    return OLA_OK;
}

static int OlaLicenceAuthAddRcToJson(cJSON *payloadJson)
{
    int ret = OlaRand((unsigned char *)&g_licenseData.devAuthRc, OLA_DEV_AUTH_RC_LEN);
    if (ret != OLA_OK) {
        OlaLogError("creat auth rc error");
        return ret;
    }

    if (strlen((char *)g_licenseData.devAuthRc) == 0) {
        OlaLogError("get auth rc error");
        return OLA_CJSON_ERR_ADD_STRING;
    }
    /* 内部函数调用点保证payloadJson不为NULL */
    if (cJSON_AddStringToObject(payloadJson, OLA_STRING_RC, (const char *)g_licenseData.devAuthRc) == NULL) {
        OlaLogError("add string error");
        return OLA_CJSON_ERR_ADD_STRING;
    }
    return OLA_OK;
}

static int OlaBuildDeviceAuthReq(OlaData *data, OlaCoapParam *param)
{
    cJSON *payloadJson = cJSON_CreateObject();
    if (payloadJson == NULL) {
        OlaLogError("create json error");
        return OLA_CJSON_ERR_CREATE_OBJECT;
    }

    char *payload = NULL;
    int ret = OlaAuthAddUdidToJson(payloadJson);
    if (ret != OLA_OK) {
        OlaLogError("create json error");
        goto FAILED;
    }

    /* T-OLA-000X.2:202X 图15 基于密钥的应用终端需要携带设备侧随机值Rc */
    if (OlaGetDevAuthType() == OLA_DEV_AUTH_TYPE_LICENSE) {
        ret = OlaLicenceAuthAddRcToJson(payloadJson);
        if (ret != OLA_OK) {
            OlaLogError("add auth rc error");
            goto FAILED;
        }
    }

    payload = cJSON_PrintUnformatted(payloadJson);
    if (payload == NULL) {
        OlaLogError("get json payload error");
        ret = OLA_CJSON_ERR_PRINT_UNFORMATTED;
        goto FAILED;
    }
    data->data = (unsigned char *)payload;
    data->len = strlen(payload);
    OlaBuildCloudRequestCoapParam(param, OLA_COAP_URI_DEVICE_AUTH, OlaDeviceAuthRspHandler, false);
    cJSON_Delete(payloadJson);
    return OLA_OK;
FAILED:
    cJSON_Delete(payloadJson);
    OlaPutNullAfterFree((void **)&payload);
    return ret;
}

static int OlaSendDeviceAuthReq(void)
{
    OlaData sendData = { NULL, 0 };
    OlaCoapParam param;
    (void)memset_s(&param, sizeof(OlaCoapParam), 0, sizeof(OlaCoapParam));
    int ret = OlaBuildDeviceAuthReq(&sendData, &param);
    if (ret != OLA_OK) {
        OlaLogWarning("build device auth req error");
        goto EXIT;
    }

    ret = OlaSendDataToCloud(&sendData, &param);
    if (ret != OLA_OK) {
        OlaLogWarning("send device auth request error");
        goto EXIT;
    }
EXIT:
    OlaPutNullAfterFree((void **)&sendData.data);
    return ret;
}

static void OlaDeviceCloudCertifyRspHandler(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    /* Certify回包 */
    if ((session == NULL) || (pkt == NULL) || !OlaIsValidCloudSession(session)) {
        OlaLogWarning("invalid param");
        return;
    }
    if (OlaGetDeviceAuthState() != DEVICE_AUTH_STATE_WAIT_CLOUD_RESPONSE) {
        OlaLogWarning("response in invalid state");
        return;
    }

    if (pkt->header.code != OLA_COAP_RESPONSE_CODE_CONTENT) {
        OlaLogWarning("coap response error[%d]", pkt->header.code);
        goto FAILED;
    }

    if ((pkt->payload.data == NULL) || (pkt->payload.len == 0)) {
        OlaLogWarning("empty response");
        goto FAILED;
    }

    if (OlaProcCloudCertifyRsp(&pkt->payload) != OLA_OK) {
        OlaLogWarning("parse dev auth resp error");
        goto FAILED;
    }
    OlaSetDeviceAuthState(DEVICE_AUTH_STATE_FINISH);
    return;
FAILED:
    OlaSetDeviceAuthState(DEVICE_AUTH_STATE_FAILED);
}

static int OlaBuildCertCloudCertifyReqJson(cJSON *json)
{
    /* 证书构包 */
    if ((strlen((char *)g_certData.certChain) == 0) || (strlen((char *)g_certData.signature) == 0)) {
        OlaLogError("get certChain or signature error");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    /* 证书链使用Base64 编码处理后转成文本 */
    /* TODO：证书链在打桩时候已经做过Base64 编码处理，这里打桩时这里先注释掉。联调完需要恢复 */
#if AUTH_CERT_CHAIN_NEED_BASE64
    OlaData certChain = { (unsigned char *)g_certData.certChain, strlen((char *)g_certData.certChain) };
    OlaData certChainOut = { NULL, 0 };
    int ret = OlaEncodeBase64Data(&certChain, &certChainOut);
    if (ret != OLA_OK) {
        OlaLogError("encode error");
        /* 返回非OLA_OK时编码函数内部已释放申请内存，外部无需释放 */
        return ret;
    }

    if (cJSON_AddStringToObject(json, OLA_STRING_CERT_CHAIN, (char *)certChainOut.data) == NULL) {
        OlaLogError("add string error");
        OlaDataFree(&certChainOut);
        return OLA_CJSON_ERR_ADD_STRING;
    }
    OlaDataFree(&certChainOut);

    if (cJSON_AddStringToObject(json, OLA_STRING_SIGNATRUE, (char *)g_certData.signature) == NULL) {
        OlaLogError("add string error");
        return OLA_CJSON_ERR_ADD_STRING;
    }

#else
    if (cJSON_AddStringToObject(json, OLA_STRING_CERT_CHAIN, (char *)g_certData.certChain) == NULL) {
        OlaLogError("add string error");
        return OLA_CJSON_ERR_ADD_STRING;
    }
    if (cJSON_AddStringToObject(json, OLA_STRING_SIGNATRUE, (char *)g_certData.signature) == NULL) {
        OlaLogError("add string error");
        return OLA_CJSON_ERR_ADD_STRING;
    }

#endif

    return OLA_OK;
}

static int OlaBuildLicenseCloudCertifyReqJson(cJSON *json)
{
    /* 共享秘钥license构包 */
    if ((strlen((char *)g_licenseData.localAuthHs)) == 0) {
        OlaLogError("auth hs error");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if (cJSON_AddStringToObject(json, OLA_STRING_HS, (char *)g_licenseData.localAuthHs) == NULL) {
        OlaLogError("add string error");
        return OLA_CJSON_ERR_ADD_STRING;
    }
    return OLA_OK;
}

static int OlaBuildCloudCertifyReq(OlaData *data, OlaCoapParam *param)
{
    cJSON *dataJson = cJSON_CreateObject();
    if (dataJson == NULL) {
        OlaLogError("create json error");
        return OLA_CJSON_ERR_CREATE_OBJECT;
    }

    int ret = OlaAuthAddUdidToJson(dataJson);
    if (ret != OLA_OK) {
        OlaLogError("create json error");
        cJSON_Delete(dataJson);
        return ret;
    }

    if (OlaGetDevAuthType() == OLA_DEV_AUTH_TYPE_LICENSE) {
        ret = OlaBuildLicenseCloudCertifyReqJson(dataJson);
    } else {
        ret = OlaBuildCertCloudCertifyReqJson(dataJson);
    }
    if (ret != OLA_OK) {
        OlaLogWarning("build certify json error");
        cJSON_Delete(dataJson);
        return OLA_CJSON_ERR_ADD_STRING;
    }

    char *payload = cJSON_PrintUnformatted(dataJson);
    if (payload == NULL) {
        OlaLogError("get json payload error");
        ret = OLA_CJSON_ERR_PRINT_UNFORMATTED;
        goto FAILED;
    }
    data->data = (unsigned char *)payload;
    data->len = strlen(payload);
    OlaBuildCloudRequestCoapParam(param, OLA_COAP_URI_CLOUD_CERTIFY, OlaDeviceCloudCertifyRspHandler, false);
    cJSON_Delete(dataJson);
    return ret;
FAILED:
    cJSON_Delete(dataJson);
    OlaPutNullAfterFree((void **)&payload);
    return ret;
}

static int OlaSendCloudCertifyReq(void)
{
    OlaData sendData = { NULL, 0 };
    OlaCoapParam param;
    (void)memset_s(&param, sizeof(OlaCoapParam), 0, sizeof(OlaCoapParam));
    int ret = OlaBuildCloudCertifyReq(&sendData, &param);
    if (ret != OLA_OK) {
        OlaLogWarning("build cloud auth certify error");
        goto EXIT;
    }

    ret = OlaSendDataToCloud(&sendData, &param);
    if (ret != OLA_OK) {
        OlaLogWarning("send cloud auth certify request error");
        goto EXIT;
    }
EXIT:
    OlaPutNullAfterFree((void **)&sendData.data);
    return ret;
}

/* 设备认证主流程 */
int OlaDeviceAuthProcess(void)
{
    int ret = OLA_STATE_RUNING;
    int errcode;
    switch (OlaGetDeviceAuthState()) {
        case DEVICE_AUTH_STATE_SEND_DEVICE_AUTH_REQUEST:
            OlaNotifySdkEvent(OLA_EVENT_CODE_M2M_AUTH_START);
            errcode = OlaSendDeviceAuthReq();
            if (errcode == OLA_OK) {
                OlaSetDeviceAuthState(DEVICE_AUTH_STATE_WAIT_CLOUD_RESPONSE);
            } else {
                OlaLogWarning("send dev auth req failed [%d]", errcode);
                ret = OLA_ERROR;
            }
            break;
        case DEVICE_AUTH_STATE_SEND_CLOUD_CERTIFY_REQUEST:
            errcode = OlaSendCloudCertifyReq();
            if (errcode == OLA_OK) {
                g_startWaitTime = 0;
                OlaSetDeviceAuthState(DEVICE_AUTH_STATE_WAIT_CLOUD_RESPONSE);
            } else {
                OlaLogWarning("send cloud certify failed [%d]", errcode);
                ret = OLA_ERROR;
            }
            break;
        case DEVICE_AUTH_STATE_WAIT_CLOUD_RESPONSE:
            errcode = OlaWaitCloudResponse(&g_startWaitTime);
            if (errcode != OLA_OK) {
                OlaLogWarning("wait response error [%d]", errcode);
                ret = OLA_ERROR;
            }
            break;
        case DEVICE_AUTH_STATE_FINISH:
            OlaNotifySdkEvent(OLA_EVENT_CODE_M2M_AUTH_SUCCESS);
            ret = OLA_OK;
            break;
        case DEVICE_AUTH_STATE_FAILED:
            OlaNotifySdkEvent(OLA_EVENT_CODE_M2M_AUTH_FAIL);
            ret = OLA_ERROR;
            break;
        default:
            OlaLogWarning("invalid auth state");
            ret = OLA_ERROR;
            break;
    }
    return ret;
}

static int OlaLicenseAuthGetData(void)
{
    /* 共享秘钥全局变量 */
    OlaData data = { NULL, 0 };
    if (OlaGetDeviceData(OLA_DATA_TYPE_AUTH_DEVICE_LICENSE, &data) != OLA_OK) {
        OlaLogError("get license data failed");
        return OLA_ERROR;
    }

    if ((data.data == NULL) || (data.len == 0) || (data.len > AUTH_LICENSE_LEN)) {
        OlaLogError("invliad len[%u]", data.len);
        OlaDataFree(&data);
        return OLA_ERROR;
    }

    if (memcpy_s(g_licenseData.license, AUTH_LICENSE_LEN, data.data, data.len) != EOK) {
        OlaLogError("memcpy_s err");
        OlaDataFree(&data);
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    OlaDataFree(&data);
    return OLA_OK;
}

static int OlaCertAuthGetData(void)
{
    /* 获取证书链 */
    OlaData data = { NULL, 0 };
    if (OlaGetDeviceData(OLA_DATA_TYPE_AUTH_DEVICE_CERT_CHAIN, &data) != OLA_OK) {
        OlaLogError("get certChain data failed");
        return OLA_ERROR;
    }

    if ((data.data == NULL) || (data.len == 0) || (data.len > DEV_CERTCHAIN_LEN)) {
        OlaLogError("invliad cert");
        OlaDataFree(&data);
        return OLA_ERROR;
    }

    if (memcpy_s(g_certData.certChain, DEV_CERTCHAIN_LEN, data.data, data.len) != EOK) {
        OlaLogError("memcpy_s err");
        OlaDataFree(&data);
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    OlaDataFree(&data);

    /* 获取证书私钥 */
    OlaData privateKey = { NULL, 0 };
    if (OlaGetDeviceData(OLA_DATA_TYPE_AUTH_DEVICE_PRIVATE_KEY, &privateKey) != OLA_OK) {
        OlaLogError("get certChain data failed");
        return OLA_ERROR;
    }

    if ((privateKey.data == NULL) || (privateKey.len == 0) || (privateKey.len > DEV_PRIVATE_KEY_LEN)) {
        OlaLogError("invliad private key");
        OlaDataFree(&privateKey);
        return OLA_ERROR;
    }

    if (memcpy_s(g_certData.privateKey, DEV_PRIVATE_KEY_LEN, privateKey.data, privateKey.len) != EOK) {
        OlaLogError("memcpy_s err");
        OlaDataFree(&privateKey);
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    OlaDataFree(&privateKey);

    return OLA_OK;
}

static void OlaLicenseDeviceAuthInit(void)
{
    (void)memset_s(&g_licenseData, sizeof(g_licenseData), 0, sizeof(g_licenseData));
    int ret = OlaLicenseAuthGetData();
    if (ret != OLA_OK) {
        OlaLogWarning("init License error");
    }
}

static void OlaCertDeviceAuthInit(void)
{
    (void)memset_s(&g_certData, sizeof(g_certData), 0, sizeof(g_certData));
    int ret = OlaCertAuthGetData();
    if (ret != OLA_OK) {
        OlaLogWarning("init Cert error");
    }
}

void OlaDeviceAuthInit(void)
{
    OlaSetDeviceAuthState(DEVICE_AUTH_STATE_SEND_DEVICE_AUTH_REQUEST);
    (void)OlaSetDevAuthType(OLA_DEV_AUTH_TYPE_CERT);

    g_startWaitTime = 0;

    if (OlaGetDevAuthType() == OLA_DEV_AUTH_TYPE_LICENSE) {
        OlaLicenseDeviceAuthInit();
    } else {
        OlaCertDeviceAuthInit();
    }
}